
#include <qapplication.h>  // For calling the static function exit(1)
			   // on failure.

#include <qmessagebox.h>
#include <qdatetime.h>
#include <qvaluelist.h>

#include "BnetDefinitions.h"
#include "BnetLogger.h"

#include "BnetPacketParser.h"


BnetLogger::BnetLogger( QObject* parent,
			const char* name )
    :
    QObject( parent, name ),
    currUser_(""),
    sniffer_( this, 0, PacketSniffer::Bnet ),
    filename_(""),
    isLogging_( false ),
    isLoggingToFile_( false ),
    logUserInChannelEnabled_( true ),
    logUserJoinLeaveEnabled_( true )
{
    // Make sure the sniffer initialized properly.
    if( !sniffer_.initSuccess() )
    {
	QMessageBox::critical( 0, tr("Initialization Failed"),
			       tr("Failed to create and initialize ") +
			       tr("packet sniffer!  Exiting...") );
	QApplication::exit(1);
    }
}

BnetLogger::~BnetLogger()
{
    // Kill the running thread and close the log file.
    this->terminate();
    logFile_.close();
}


bool BnetLogger::setLogging( const bool& logging )
{
    if( logging == false ) 
    {
	// Turn off logging to file as well.
	setLoggingToFile( false );
    }
    QMutexLocker locker( &loggingMutex_ );
    isLogging_ = logging;
    return true;
}

bool BnetLogger::setLoggingToFile( const bool& loggingToFile, 
				   const QString& filename,
				   const bool& logUserInChannelEnabled,
				   const bool& logUserJoinLeaveEnabled )
{
    loggingToFileMutex_.lock();
    isLoggingToFile_ = loggingToFile;
    filename_ = filename;
    logUserInChannelEnabled_ = logUserInChannelEnabled;
    logUserJoinLeaveEnabled_ = logUserJoinLeaveEnabled;

    // If the user of this function tried to turn on file-logging, but
    // didnt' set a filename, keep file-logging off.
    if( isLoggingToFile_ == true && filename_ == "" )
    {
	isLoggingToFile_ = false;
    }
    loggingToFileMutex_.unlock();
    
    // If it's false, close the file.  If it's true, open it.
    if( isLoggingToFile() )
    {
	// Close the file, just in case it is already open.
	logFile_.close();
	// Now open it.
	logFile_.setName( filename_ );
	logFile_.open( IO_WriteOnly | IO_Append );
    }
    else
    {
	// Logging to file needs to be turned off.  Just close the file.
	logFile_.close();
    }

    if( isLogging() == false && 
	isLoggingToFile() == true )
    {
	// Logging isn't on, turn it on!
	return setLogging( true );
    }
    return true;
}

bool BnetLogger::isLogging()
{
    QMutexLocker locker( &loggingMutex_ );
    return isLogging_;
}

bool BnetLogger::isLoggingToFile()
{
    QMutexLocker locker( &loggingToFileMutex_ );
    return isLoggingToFile_;
}


QString BnetLogger::getFilename()
{
    return filename_;
}


void BnetLogger::run()
{
    while( true )
    {
	if( this->isLogging() )
	{
	    // Sniff some byte arrays.
	    QValueList<QByteArray> arrayList = sniffer_.sniff();

	    // Open a stream to the log file.
	    QTextStream chatlog( &logFile_ );

	    // Iterate over the byte arrays.
	    for( QValueList<QByteArray>::iterator iter = arrayList.begin();
	     iter != arrayList.end();
	     iter++ )
	    {
		// Parse the byte array to get a BnetMessage.
		BnetPacketParser parserObj;
		BnetMessage msg = parserObj.parsePacket( *iter );

		// Do logging-type events for the message types.
		if( isLoggingToFile() && getFilename() != "" )
		{
		    // Branch from the message type.
		    if( msg.type == BnetMessage::Null )
		    {
			// Log nothing.
		    }
		    else if( msg.type == BnetMessage::ChatEvent ||
			     msg.type == BnetMessage::FriendJoinedGame )
		    {
			// Big switch statement for the eventID.
			switch( msg.eventID )
			{
			case EID_SHOWUSER:
			    if( logUserInChannelEnabled_ )
			    {
				chatlog << timestampStr() 
					<< " User in channel: " 
					<< msg.user << "\n";
			    }
			    break;
			case EID_JOIN:
			    if( logUserJoinLeaveEnabled_ )
			    {
				chatlog << timestampStr() 
					<< " User joined channel: "
					<< msg.user << "\n";
			    }
			    break;
			case EID_LEAVE:
			    if( logUserJoinLeaveEnabled_ )
			    {
				chatlog << timestampStr() 
					<< " User left channel: "
					<< msg.user << "\n";
			    }
			    break;
			case EID_WHISPER:
			    chatlog << timestampStr() << " Whisper from <" 
				    << msg.user
				    << "> " << msg.text << "\n";
			    break;
			case EID_TALK:
			    chatlog << timestampStr() << " <" << msg.user 
				    << "> "  << msg.text << "\n";
			    break;
			case EID_BROADCAST:
			    chatlog << timestampStr() << " Server broadcast: "
				    << msg.user << " " << msg.text << "\n";
			    break;
			case EID_CHANNEL:
			    chatlog << timestampStr() << " Channel:  " 
				    << msg.text << "\n";
			    break;
			case EID_USERFLAGS:
			    if(msg.flags == USER_CHANNELOP) 
			    {
				chatlog << timestampStr() 
					<< " You have channel ops." << "\n";
			    }
			    break;
			case EID_WHISPERSENT:
			    chatlog << timestampStr() << " Whisper sent <" 
				    << msg.user << "> " << msg.text << "\n";
			    break;
			case EID_CHANNELFULL:
			    chatlog << timestampStr() 
				    << " Channel Full." << "\n";
			    break;
			case EID_CHANNELDOESNOTEXIST:
			    chatlog << timestampStr() 
				    << " Channel does not exist." << "\n";
			    break;
			case EID_CHANNELRESTRICTED:
			    chatlog << timestampStr() 
				    << " Channel is restricted." << "\n";
			    break;
			case EID_INFO:
			    chatlog << timestampStr() << " Info: "
				    << " " << msg.text << "\n";
			    break;
			case EID_ERROR: 
			    chatlog << timestampStr() << " Error: " << msg.user
				    << " " << msg.text << "\n";
			    break;
			case EID_EMOTE:
			    chatlog << timestampStr() << " Emote: " 
				    << msg.user 
				    << "    " << msg.text << "\n";
			    break;
			default:
			    chatlog << timestampStr() 
				    << " Unknown event ID." << "\n";
			    break;
			}
		    }
		    else if( msg.type == BnetMessage::EnterChat )
		    {
			// Log the enter-chat.
			chatlog << timestampStr() << " BNET:  "
				<< "You are logged in as " << msg.user << "\n";
		    }

		    else if( msg.type == BnetMessage::LeaveChat )
		    {
			chatlog << timestampStr() << " BNET:  "
				<< "You logged off username " 
				<< currUser_ << "\n";
		    }
		    
		    else if( msg.type == BnetMessage::ChatCommand )
		    {
			chatlog << timestampStr() << "<"
				<< currUser_ << "> " << msg.text << "\n";
		    }
		    
		    else if( msg.type == BnetMessage::NonChatEvent )
		    {
			chatlog << timestampStr() << " SERVER:  " 
				<< msg.text << "\n";
		    }

		    else if( msg.type == BnetMessage::System )
		    {
			chatlog << timestampStr() << " SYSTEM:  " 
				<< msg.text << "\n";
		    }
		}
		
		// Do action-type events for the message types.
		// Branch from the message type.

		if( true )  // Pointless if() for code navigation & structure.
		{
		    if( msg.type == BnetMessage::Null )
		    {
		    }
		    else if( msg.type == BnetMessage::ChatEvent ||
			     msg.type == BnetMessage::FriendJoinedGame )
		    {
		    }
		    else if( msg.type == BnetMessage::EnterChat )
		    {
			currUser_ = msg.user;
		    }
		    else if( msg.type == BnetMessage::LeaveChat )
		    {
			currUser_ = "";
		    }
		    else if( msg.type == BnetMessage::ChatCommand )
		    {
		    }
		    else if( msg.type == BnetMessage::NonChatEvent )
		    {
		    }
		    else if( msg.type == BnetMessage::System )
		    {
		    }
		}
		
	    
		// Emit the signal if it's a non-null message.
		if( msg.type != BnetMessage::Null )
		{
		    emit logging( msg );
		}
		
		// Not sure if this is needed, but I read somewhere it
		// might negate any thread delays when using an emit.
		QApplication::postEvent( qApp, new QEvent( QEvent::None ) );
	    }
	    // Flush the log file.
	    logFile_.flush();
	}
	else
	{
	    this->sleep( 1 );
	}
    }
}


QString BnetLogger::timestampStr()
{
    QDateTime now = QDateTime::currentDateTime();
    return now.toString( "[M/d hh:mm:ss] " );
}

